home *** CD-ROM | disk | FTP | other *** search
- #ifndef lint
- static char *RCSid = "$Header: ntptime.c,v 1.10 89/02/19 16:08:56 bww Exp $";
- #endif
-
- #define VERSIONSTRING "ntptime v 1.12"
- /*
- * ntptime - get/set the time via ntp
- *
- ****************************************************************
- * HISTORY
- * $Log: ntptime.c,v $
- * Revision 1.10 89/02/19 16:08:56 bww
- * SSP headers. Added "-t" flag.
- * [89/02/19 16:07:44 bww]
- *
- * Revision 1.9 89/02/09 18:30:36 bww
- * Turn exit code 1 into 2 to avoid "not found" territory.
- *
- * Revision 1.8 88/11/26 16:26:05 bww
- * Don't set the time if 1/3 of the hosts disagree and the change is big.
- *
- * Revision 1.7 88/11/11 16:07:18 bww
- * Upgraded to latest UMD release.
- *
- * Revision 1.6 88/09/09 19:33:40 bww
- * Use filter index zero first.
- *
- * Revision 1.5 88/08/25 14:52:50 bww
- * Exit codes [0 .. 10] give (decreasing) confidence in result.
- *
- * Revision 1.4 88/08/22 17:17:05 bww
- * Bumped size of receive buffer.
- *
- * Revision 1.3 88/08/20 17:00:23 bww
- * Fixed interrupt handling and added timeouts. Also added
- * beginnings of a sanity check before setting the time.
- *
- * Revision 1.2 88/08/16 19:11:09 bww
- * Added definition of "debug".
- *
- * Revision 1.1 88/07/25 23:32:19 bww
- * Initial revision
- *
- */
-
- #include <Types.h>
- #include <Events.h>
- #include <Math.h>
- #include <fcntl.h>
- #include <stdio.h>
- #include <ErrMgr.h>
- #include <CursorCtl.h>
- #include <Errors.h>
- #include <Devices.h>
- #include <MacTCPCommonTypes.h>
- #include <UDPPB.h>
- #include <TCPPB.h>
- #include <GetMyIPAddr.h>
- #include <AddressXlation.h>
- #include <Time.h>
- #include "ntp.h"
-
- /*
- * exit codes:
- * EX_OK : all ok
- * EX_OK + [1 .. EX__RANGE] : unreliable range - time set
- * EX_OK + EX__RANGE + 1 : unreliable - time not set
- * EX_OK + EX__RANGE + 2 : unreliabe - didn't converge (timer expired)
- * EX__SIGBASE + s : interrupted with signal s
- * others : <sysexits.h> codes
- */
- #define EX_OK 0
- #define EX__RANGE 10
- #define EX__SIGBASE 32
-
- #define EX_OSERR 11
- #define EX_OSFILE 12
- #define EX_USAGE 13
-
- #define DEFTOWAIT 15 /* # seconds */
-
- #define NTPD_PORT 123 /* 123/udp */
-
- #define NIL 0
- #define TICKPERSECOND 60
-
- static char *name;
- static StreamPtr myudpstream = 0;
- static udp_port myudpport = 0;
- static int doset = 0;
- static int towait = DEFTOWAIT;
- static int verbose = 0;
- static char conffile[] = NTPINITFILE;
- static struct ntp_peer *peers = 0;
- static short refnum = 0;
-
- struct ntp_peer *confinit();
- void setthetime();
- void chkserver();
- void usage();
-
- extern int errno;
- #ifdef DEBUG
- int debug;
- #endif
-
- extern char *malloc();
- extern char *ctime();
- extern double ul_fixed_to_double();
- extern double minimum_filter();
- extern void ntpsend(struct ntp_peer *peer);
- extern void packet();
- extern void setminuteseast(long num);
- extern char * macctime(const time_t *unixsecs);
-
- void bzero(char * bytes, int len)
- {
- register char *p;
-
- for (p = bytes; len > 0; len--,p++) {
- *p = '\0';
- }
- }
-
- char *inet_ntoa(ip_addr addr)
- {
- static char buf[100];
-
- if (AddrToStr(addr,buf) != noErr) {
- return "?use resolver?";
- } else {
- return buf;
- }
- }
-
- void clean_exit(int code)
- {
- UDPiopb udppb;
-
- if (myudpstream != 0) {
- bzero((char *)&udppb,sizeof(udppb));
- udppb.ioCompletion = NIL;
- udppb.ioCRefNum = refnum;
- udppb.csCode = UDPRelease;
- udppb.udpStream = myudpstream;
- udppb.csParam.create.userDataPtr = NIL;
-
-
- if (PBControl((ParmBlkPtr)&udppb,false) != noErr) {
- fprintf(stderr,"Trouble releasing udp stream in clean_exit(%d)\n",code);
- }
- }
- (void)CloseResolver(); /* ignore errors */
- if (code != EX_OK) {
- fprintf(stderr,"clean_exit(%d)\n",code);
- }
- exit(code);
- }
-
- void FailOSErr(msg, err)
- char *msg;
- OSErr err;
- {
- if (err != noErr) {
- fprintf(stderr,"%s errcode=%d\n",msg,err);
- clean_exit (-1);
- }
- }
-
- void ReportOSErr(msg, err)
- char *msg;
- OSErr err;
- {
- if (err != noErr) {
- fprintf(stderr,"%s errcode=%d\n",msg,err);
- }
- }
-
- void
- waitforonesecond()
- {
- register unsigned long ticks;
- for (ticks = TickCount(); TickCount() < (ticks + TICKPERSECOND);) {
- EventRecord theEvent;
-
- (void)EventAvail(0,&theEvent);
- packet(); /* checks for incoming packets */
- }
- }
-
- void
- main(ac, av)
- int ac;
- char **av;
- {
- register char *p;
- register struct ntp_peer *thispeer;
- UDPiopb udppb;
- unsigned long ticks;
-
- InitCursorCtl(nil);
- name = (ac > 0) ? (ac--, *av++) : (ac = 0, "ntptime");
- while (ac && *(p = *av) == '-') {
- while (*++p)
- switch (*p) {
- case 's':
- doset = 1;
- break;
- case 't':
- if (*(p+1) == 0) {
- if (ac == 1)
- usage();
- towait = atoi((--ac, *++av));
- } else if (*++p < '0' || '9' < *p)
- usage();
- else {
- towait = atoi(p);
- while ('0' <= *++p && *p <= '9')
- continue;
- --p;
- }
- break;
- case 'v':
- verbose++;
- break;
- case '?':
- default:
- usage();
- }
- ac--, av++;
- }
- if (ac)
- usage();
-
- if (verbose) {
- fprintf(stdout,"[ %s ]\n",VERSIONSTRING);
- (void) fflush(stdout);
- }
- FailOSErr("OpenResolver", OpenResolver(NIL));
- FailOSErr("Can't open driver",opendriver(".IPP",&refnum));
-
- /* grab a udp port, to use for sending packets. NoDelay, Async */
-
- p = malloc(4096);
- if (p == NULL) {
- fprintf(stderr,"malloc fails\n");
- clean_exit(-1);
- }
- bzero((char *)&udppb,sizeof(udppb));
- udppb.ioCompletion = NIL;
- udppb.ioCRefNum = refnum;
- udppb.csCode = UDPCreate;
- udppb.csParam.create.rcvBuff = p;
- udppb.csParam.create.rcvBuffLen = 4096;
- udppb.csParam.create.notifyProc = NIL;
- udppb.csParam.create.localPort = 0;
- udppb.csParam.create.userDataPtr = 0;
-
- FailOSErr("UDPCreate Fails",PBControl((ParmBlkPtr)&udppb,false));
-
- myudpstream = udppb.udpStream;
- myudpport = udppb.csParam.create.localPort;
-
- /* check if the server is running */
- if (doset)
- chkserver(NTPD_PORT);
-
- peers = confinit(NTPD_PORT);
-
- /* now, every second, pool everyone */
- for (ticks = TickCount(); TickCount() < ticks + (towait * TICKPERSECOND); waitforonesecond()) {
- for (thispeer = peers; thispeer; thispeer = thispeer->next_peer) {
- if (++thispeer->timer >= NTP_GRANPOLL) {
- ntpsend(thispeer);
- }
- packet(); /* checks for incoming packets */
- }
- }
- if (doset) {
- /* we shouldn't get here if doset is true */
- fprintf(stderr, "%s: not enough time to converge - clock not set\n", name);
- clean_exit(EX_OK + EX__RANGE + 2);
- }
- clean_exit(EX_OK);
- }
-
-
- void
- ntpsend(peer)
- register struct ntp_peer *peer;
- {
- struct ntpdata pkt;
- struct timeval tp;
- UDPiopb udppb;
- wdsEntry wds[2];
- OSErr err;
-
- SpinCursor(8);
-
- bzero((char *) &pkt, sizeof(pkt));
- pkt.status = NTPVERSION_1;
- pkt.stratum = UNSPECIFIED;
- pkt.poll = 0;
- pkt.org.int_part = pkt.org.fraction = 0;
- pkt.rec.int_part = pkt.rec.fraction = 0;
- if (gettimeofday(&tp, (struct timezone *) 0) == -1) {
- perror(name);
- return;
- }
- tstamp(&pkt.xmt, &tp);
-
- bzero((char *)&udppb,sizeof(udppb));
- udppb.ioCompletion = NIL;
- udppb.ioCRefNum = refnum;
- udppb.csCode = UDPWrite;
- udppb.udpStream = myudpstream;
- udppb.csParam.send.remoteHost = peer->src;
- udppb.csParam.send.remotePort = peer->port;
- udppb.csParam.send.userDataPtr = NIL;
-
- /* build write-data structure */
- wds[0].length = sizeof(pkt);
- wds[0].ptr = (char *) &pkt;
- wds[1].length = 0;
-
- udppb.csParam.send.wdsPtr = (Ptr)&wds;
-
-
- err = PBControl((ParmBlkPtr)&udppb,false);
- if (err == ipDestDeadErr) { /* are there other codes here we should special case? */
- fprintf(stderr,"Host %s not responding, errcode=%d\n",inet_ntoa(peer->src),err);
- } else if (err != noErr) {
- fprintf(stderr,"UDPsend fails errcode=%d\n",err);
- }
-
- peer->timer = 0;
- peer->pkt_sent++;
- }
-
- void
- packet()
- {
- register struct ntp_peer *p;
- register int i;
- struct ntpdata pkt;
- struct timeval tp;
- double t1, t2, t3, t4;
- double offset, delay;
- double t_offset[SAMPLES], t_delay[SAMPLES];
- int len;
- UDPiopb udppb;
- OSErr err;
-
- SpinCursor(8);
-
- bzero((char *)&udppb,sizeof(udppb));
- udppb.ioCompletion = NIL;
- udppb.ioCRefNum = refnum;
- udppb.csCode = UDPRead;
- udppb.udpStream = myudpstream;
- udppb.csParam.receive.timeOut = 2; /* minimum time */
- udppb.csParam.receive.secondTimeStamp = 0;
- udppb.csParam.receive.userDataPtr = NIL;
- err = PBControl((ParmBlkPtr)&udppb,false);
-
- if (err == commandTimeout) {
- fprintf(stderr,"packet:UDPRead times out\n");
- return;
- } else if (err != noErr) {
- fprintf(stderr,"packet:UDPRead fails\n");
- return;
- }
-
- /* copy bytes into pkt */
- len = udppb.csParam.receive.rcvBuffLen;
-
- memcpy (&pkt, udppb.csParam.receive.rcvBuff,
- (sizeof(pkt) > len) ? (len) : (sizeof(pkt)));
-
- /* release buffer space */
- udppb.csCode = UDPBfrReturn;
- if (PBControl((ParmBlkPtr)&udppb,false) != noErr) {
- fprintf(stderr,"packet:UDPBfrReturn fails\n");
- }
- if (sizeof(pkt) != len) {
- fprintf(stderr,"packet was %d bytes, expected %d\n",len,sizeof(pkt));
- }
-
- if (gettimeofday(&tp, (struct timezone *) 0) == -1) {
- perror(name);
- return;
- }
- if (pkt.stratum == UNSPECIFIED || pkt.stratum > 8
- || (pkt.status & LEAPMASK) == ALARM)
- return;
-
- for (p = peers; p; p = p->next_peer)
- if (p->src == udppb.csParam.receive.remoteHost)
- break;
- if (p == 0 || udppb.csParam.receive.remotePort != p->port)
- return;
-
- p->pkt_rcvd++;
- tstamp(&p->rec, &tp);
-
- if ((pkt.org.int_part == 0 && pkt.org.fraction == 0)
- || (pkt.rec.int_part == 0 && pkt.rec.fraction == 0))
- return;
-
- t1 = ul_fixed_to_double(&pkt.org);
- t2 = ul_fixed_to_double(&pkt.rec);
- t3 = ul_fixed_to_double(&pkt.xmt);
- t4 = ul_fixed_to_double(&p->rec);
- offset = ((t2 - t1) + (t3 - t4)) / 2.0;
- delay = (t2 - t1) - (t3 - t4);
-
- i = p->filter.index++ % SAMPLES;
- p->filter.offset[i] = offset;
- p->filter.delay[i] = delay;
-
- for (i = 0; i < SAMPLES; i++) {
- t_delay[i] = p->filter.delay[i];
- t_offset[i] = p->filter.offset[i];
- }
- p->dispersion = minimum_filter(t_delay, t_offset, SAMPLES);
- p->delay = t_delay[0];
- p->offset = t_offset[0];
-
- if (p->dispersion <= PEER_THRESHOLD)
- setthetime(p);
-
- ntpsend(p);
- }
-
- int
- sanitychk(best)
- register struct ntp_peer *best;
- {
- register struct ntp_peer *p;
- register int cnt = 0, bad = 0;
-
- for (p = peers; p; p = p->next_peer) {
- if (p == best || p->pkt_rcvd == 0)
- continue;
- cnt++;
- if (fabs(best->offset - p->offset) < WAYTOOBIG/2)
- continue;
- fprintf(stdout, "Warning: %s differs by %f seconds\n",
- inet_ntoa(p->src),
- best->offset - p->offset);
- bad++;
- }
- if (cnt == 0 || bad == 0)
- return 0;
- return (bad * EX__RANGE) / cnt;
- }
-
- void
- setthetime(best)
- register struct ntp_peer *best;
- {
- register struct ntp_peer *p;
- register int code;
- double offset;
- struct timeval tv;
-
- if (verbose)
- for (p = peers; p; p = p->next_peer)
- fprintf(stdout, "[ %16s%c %lu/%lu (%f/%f) ]\n",
- inet_ntoa(p->src),
- p == best ? '*' : ' ',
- p->pkt_sent, p->pkt_rcvd,
- p->dispersion, p->offset);
- offset = best->offset;
- if (doset == 0) {
- fprintf(stdout, "Your clock is running %.3f seconds ", fabs(offset));
- fprintf(stdout, "(%.3f minutes) %s\n",
- fabs(offset) / 60.0, (offset<0.0) ? "fast" : "slow");
- clean_exit(EX_OK);
- }
- code = sanitychk(best);
- if (code > 3 && fabs(offset) > WAYTOOBIG) {
- fprintf(stderr, "%s: unreliable time - clock not set\n", name);
- clean_exit(EX_OK + EX__RANGE + 1);
- }
- if (gettimeofday(&tv, (struct timezone *) 0) == -1) {
- perror(name);
- clean_exit(EX_OSERR);
- }
- offset += tv.tv_sec;
- offset += tv.tv_usec / 1000000.0;
- tv.tv_sec = offset;
- tv.tv_usec = (offset - tv.tv_sec) * 1000000;
- if (settimeofday(&tv, (struct timezone *) 0) == -1) {
- fprintf(stderr, "%s: can't set the time\n", name);
- }
- fputs(macctime(&tv.tv_sec), stdout);
- if (code)
- fprintf(stdout, "*** CHECK THE DATE ***\n");
- clean_exit(EX_OK + code);
- }
-
- struct ntp_peer *
- confinit(port)
- ip_port port;
- {
- register struct ntp_peer *p, *q;
- register FILE *f;
- register int i;
- char buf[BUFSIZ], word[BUFSIZ];
- ip_addr src;
- struct hostInfo hp;
- long numminuteseast = 0;
-
- p = NIL;
- if ((f = fopen(conffile, "r")) == NULL) {
- perror(conffile);
- clean_exit(EX_OSFILE);
- }
- while (fgets(buf, sizeof(buf), f) != NULL) {
- if (sscanf(buf, "%s", word) != 1)
- continue;
- if (strcmp(word, "peer") == 0) {
- if (sscanf(buf, "%*s %s", word) != 1) {
- fprintf(stderr, "%s: bad peer line\n", name);
- clean_exit(EX_OSFILE);
- }
- if (StrToAddr(word,&hp,NIL,NIL) != noErr) {
- fprintf(stderr, "%s: unknown host\n", word);
- clean_exit(EX_OSFILE);
- }
- src = hp.addr[0];
- for (q = p; q; q = q->next_peer)
- if (q->src == src)
- break;
- if (q)
- continue;
- if ((q = (struct ntp_peer *) malloc(sizeof(*q))) == 0) {
- perror(name);
- clean_exit(EX_OSERR);
- }
- q->src = src;
- q->port = port;
- q->timer = NTP_GRANPOLL;
- q->dispersion = NTP_MAXDISP;
- q->offset = 0.0;
- q->delay = 0.0;
- q->filter.index = 0;
- for (i = 0; i < SAMPLES; i++) {
- q->filter.offset[i] = 0.0;
- q->filter.delay[i] = NTP_MAXDELAY;
- }
- q->pkt_sent = 0;
- q->pkt_rcvd = 0;
- q->next_peer = p;
- p = q;
- } else if (strcmp(word, "minuteseast") == 0) {
- if (sscanf(buf, "%*s %d", &numminuteseast) != 1) {
- fprintf(stderr, "%s: bad minuteseast line\n", name);
- clean_exit(EX_OSFILE);
- }
- }
- }
- setminuteseast(numminuteseast);
- (void) fclose(f);
- return p;
- }
-
-
- void
- chkserver(port)
- int port;
- {
-
- UDPiopb udppb;
- char dummy[2048];
-
- /* grab a udp port, to use for sending packets. NoDelay, Async */
-
- bzero((char *)&udppb,sizeof(udppb));
- udppb.ioCompletion = NIL;
- udppb.ioCRefNum = refnum;
- udppb.csCode = UDPCreate;
- udppb.csParam.create.rcvBuff = &dummy;
- udppb.csParam.create.rcvBuffLen = 2048;
- udppb.csParam.create.notifyProc = NIL;
- udppb.csParam.create.localPort = port;
- udppb.csParam.create.userDataPtr = NIL;
-
- if (PBControl((ParmBlkPtr)&udppb,false) == noErr) {
- udppb.ioCompletion = NIL;
- udppb.ioCRefNum = refnum;
- udppb.csCode = UDPRelease;
- /* leave stream pointer alone */
- udppb.csParam.create.userDataPtr = NIL;
-
- if (PBControl((ParmBlkPtr)&udppb,false) != noErr) {
- fprintf(stderr,"Trouble releasing test stream in chkserver()\n");
- }
- } else {
- fprintf(stderr, "Warning: ntp server active\n");
- }
- }
-
-
- void
- usage()
- {
- fprintf(stderr, "Usage: %s [-v] [-s] [-t secs]\n", name);
- clean_exit(EX_USAGE);
- }
-